#include <iostream>
#include <fstream>
#include <cutil_inline.h>
#include <vector_types.h>
#include <cuda.h>
#include <string.h>
#include <math.h>
#include <sys/time.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>


using namespace std;
extern "C" void runTest(unsigned int arraysize, unsigned int *b_h, unsigned int *b_d, unsigned int nmer_size);
extern "C" void compareSequences (unsigned int nmer_size, unsigned int ** bs_array,
				  unsigned int num_of_bs, unsigned int * bs_lengths);
//TODO: DO SOMETHING WITH THESE
int block_pos = 0;
int current_block = 0;
int arraylength;

/**
 * Adds an element to the current element block (int)
 * or a new block if previous one is full
 //TODO: detect last, incomplete block
 **/
void addElement(char element, unsigned int *bitsequence) {
	int element_code;
	switch (element) {
		case 'A': 
			element_code = 0;
			break;
		case 'T': 
			element_code = 1;
			break;
		case 'C': 
			element_code = 2;
			break;
		case 'G': 
			element_code = 3;
			break;
		default:
			element_code = 0;
	}
	if (block_pos > 30) {
		//cout << "b["<<current_block<<"]: "<< bitsequence[current_block] << endl;
		current_block++;
		block_pos = 0;
	}
	bitsequence[current_block] <<= 2;
	bitsequence[current_block] |= element_code;
	block_pos += 2;
}

/**
 * Read FASTA file and convert to bitsequence.
 * Determines bitsequence array size by file size.
 **/
//TODO: make this void, pass array by reference
//TODO: change arraysize to arraylength
unsigned int * readFastaToArray(char * filename, unsigned int &bs_length) {
	unsigned int * bitsequence;
	block_pos = 0;
	current_block = 0;
	// Get file length ==========================================
	struct stat st;
	stat(filename, &st);
	int filelength = st.st_size;
	cout << "File Length: " << filelength << endl;
	
	// Open file ================================================
	ifstream in;
	in.open(filename);
	if (!in.is_open()) {
		cout << "ERROR: readFastaToArray() cannot open " << filename << endl;
		exit(1);
	}
	// Check for FASTA format ===================================
	char s[555];
	char c;
	in.getline(s,555);
	if(in.eof()) {
		cout<<"ERROR: readFastaToArray(): "<<filename<<" not FASTA format!\n ";
		exit(1);
	}
	cout<<s<<endl;
	if(s[0]!='>') {
		cout<<"ERROR: readFastaToArray(): "<<filename<<" not FASTA format!\n ";
		exit(1);
	}
	cout << strlen(s) << endl; // Get header length
	
	// Calculate required integer blocks ========================
	int headerLength = strlen(s);
	unsigned int numElements = filelength - headerLength; //TODO: innaccurate (newlines)
	unsigned int numBlocksRequired = ceil((numElements / ((sizeof(unsigned int)*8)/2)));
	cout << "number of elements: " << numElements << endl;
	cout << "blocks required: " << numBlocksRequired << endl;
	arraylength = numBlocksRequired;
	
	
	
	// Initialize bitsequence to zeros===========================
	cout << "Allocating " << numBlocksRequired << endl;
	bitsequence = new unsigned int[numBlocksRequired]; 
	//size_t size = arraylength*sizeof(int);
	//bitsequence = (unsigned int *)calloc(arraylength, size);
	
	// Reads in each element and calls addElement() =============
	unsigned int nonCharacters = 0;
	while(in.peek()!=EOF) {
		in.get(c);
		if(c=='>') {
			in.getline(s,555);
			nonCharacters += strlen(s);
			//cout<<s<<endl;
		}
		else if(isspace(c)==0){
			//cout << c;
			addElement(c, bitsequence);
			//cout << bitsequence[current_block] << endl;
		}
		else if(isspace(c)==1) {
			nonCharacters++;
		}
			
	}
	cout << "nonCharacters: " << nonCharacters << endl;
	unsigned int removeBlocks = floor(nonCharacters / ((sizeof(unsigned int)*8)/2));
	unsigned int newSize = (arraylength*sizeof(int)) - (removeBlocks*sizeof(int));
	cout << "Removing " << removeBlocks << endl;
	bs_length = numBlocksRequired-removeBlocks;
	bitsequence = (unsigned int *)realloc(bitsequence, newSize);
	in.close();
	// TEST: PRINT BITSEQUENCE
	//for (int i = 0; i < 11; i++) cout << i << ": " << bitsequence[i] << endl;
	return bitsequence;
}



int main() {


	// TIMING =============================
	 struct timeval tempo1, tempo2;
	long elapsed_utime;    /* elapsed time in microseconds */
	long elapsed_mtime;    /* elapsed time in milliseconds */
	long elapsed_seconds;  /* diff between seconds counter */
	long elapsed_useconds; /* diff between microseconds counter */
	//======================================
    
    
	unsigned int **bitSeq_array;
	bitSeq_array = new unsigned int *[6];
	unsigned int *bs_lengths = new unsigned int [6];
	cout << "Converting to bitseq" << endl;
	bitSeq_array[0] = readFastaToArray("dros/dana-all-chromosome-r1.3.fasta", bs_lengths[0]);
	bitSeq_array[1] = readFastaToArray("dros/dere-all-chromosome-r1.3.fasta", bs_lengths[1]);
	bitSeq_array[2] = readFastaToArray("dros/dgri-all-chromosome-r1.3.fasta", bs_lengths[2]);
	bitSeq_array[3] = readFastaToArray("dros/dmel-all-chromosome-r5.23.fasta", bs_lengths[3]);
	bitSeq_array[4] = readFastaToArray("dros/dmoj-all-chromosome-r1.3.fasta", bs_lengths[4]);
	bitSeq_array[5] = readFastaToArray("dros/dper-all-chromosome-r1.3.fasta", bs_lengths[5]);
	//bitSeq_array[6] = readFastaToArray("dros/dpse-all-chromosome-r2.6.fasta", bs_lengths[6]);
	//bitSeq_array[7] = readFastaToArray("dros/dsec-all-chromosome-r1.3.fasta", bs_lengths[7]);
	//bitSeq_array[8] = readFastaToArray("dros/dsim-all-chromosome-r1.3.fasta", bs_lengths[8]);
	//bitSeq_array[9] = readFastaToArray("dros/dvir-all-chromosome-r1.2.fasta", bs_lengths[9]);
	//bitSeq_array[10] = readFastaToArray("dros/dwil-all-chromosome-r1.3.fasta", bs_lengths[10]);
	//bitSeq_array[11] = readFastaToArray("dros/dyak-all-chromosome-r1.3.fasta", bs_lengths[11]);
	
	//bitSeq_array[5] = readFastaToArray("NC_004108.fas", bs_lengths[5]);
	cout << "length0: " << bs_lengths[0] << endl;
	gettimeofday(&tempo1, NULL); //TIMING
	//RUN
	compareSequences(14, bitSeq_array, 6, bs_lengths);
	gettimeofday(&tempo2, NULL); //TIMING
	
	elapsed_seconds  = tempo2.tv_sec  - tempo1.tv_sec;
	elapsed_useconds = tempo2.tv_usec - tempo1.tv_usec;
	elapsed_utime = (elapsed_seconds) * 1000000 + elapsed_useconds;
	elapsed_mtime = ((elapsed_seconds) * 1000 + elapsed_useconds/1000.0) + 0.5;
	printf("Elapsed time = %ld milliseconds\n", elapsed_mtime);
	
	//unsigned int *b_d;
	//unsigned int *bitsequence = readFastaToArray("test.fas",bs_lengths[0]);
	//runTest(arraysize, bitsequence, b_d,8);
	


}









